package gsolver;

import gproblem.GSupplyLinkProblem;

import gsolution.GSupplyLinkSolution;

import gsolver.GSolver;
import gsolver.GChromosome;
import gsolver.GCrossoverBPRX;
import gsolver.GMutation;
import gsolver.GFirstFitModifie;

import org.apache.commons.math.genetics.GeneticAlgorithm;
import org.apache.commons.math.genetics.SelectionPolicy;
import org.apache.commons.math.genetics.Chromosome;

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Collections;

public class GeneticSolver extends GSolver {
    static final public int populationLimit = 100;
    static final public int tauxElitisme = 25;
    static final public int eliteLimite = populationLimit * tauxElitisme / 100;
    static final public int delaiElitisme = 5;
    static final public double tauxCroisement = 1/2;
    static final public double tauxMutation = 1/2;

    public GeneticSolver() {
	super();
    }

    public GeneticSolver(GSupplyLinkProblem problem) {
	super(problem);
    }
    
    @Override
    public void solve() {
	GChromosome.setProblem(getProblem());

	GeneticAlgorithm ga = new GeneticAlgorithm(new GCrossoverBPRX(rand),
						   tauxCroisement,
						   new GMutation(rand),
						   tauxMutation,
						   new GSelectionRoulette(rand));
	GPopulation population = populationInitiale();

	while (true) {
	    GSupplyLinkSolution best = 
		population.getFittestGChromosome().toGSupplyLinkSolutionSimplex();
	    if (getBestSolution() == null || best.evaluate() <= getBestSolution().evaluate()) {
		setBestSolution(best);
	    }
	    population = (GPopulation)(ga.nextGeneration(population));
	}
    }
    
    /* Génère une population initiale en appliquant l'algorithme First Fit 
       modifiè à populationLimit permutations aléatoires de jobs. */
    private GPopulation populationInitiale() {
	PriorityQueue<Chromosome> chromosomes = new PriorityQueue<Chromosome>(populationLimit);
	ArrayList<Integer> jobs = new ArrayList<Integer>(getProblem().getN());

	for (int i = 0; i < getProblem().getN(); i++) {
	    jobs.add(i);
	}

	while (chromosomes.size() < populationLimit) {
	    ArrayList<Integer> permutation = new ArrayList<Integer>(jobs);

	    Collections.shuffle(permutation, rand);
	    
	    ArrayList<Lot> representation = new ArrayList<Lot>();

	    GFirstFitModifie.insererFFM(representation, permutation.toArray(new Integer[0]));

	    chromosomes.offer((Chromosome)(new GChromosome(representation)));
	}

	return new GPopulation(chromosomes, delaiElitisme, new PriorityQueue<Chromosome>());
    }
}